iT邦幫忙

2025 iThome 鐵人賽

DAY 20
0
Rust

Rust 30 天養成計畫:從零到 CLI 專案系列 第 20

Day 20:小專案— 字數統計工具

  • 分享至 

  • xImage
  •  

專案主題
製作一個簡單的命令列應用程式(Command-line App),可以讀取文字檔並輸出:

  • 行數
  • 單字數
  • 字元數

1. 規劃
我希望輸入部分可以直接打上檔案名稱後就獲取這個檔案的資料,輸出部分則是分成三行輸出行數、單字數、字元數。關於錯誤處理的部分,如果沒有輸入檔名要給提示訊息,若檔案不存在則要印出錯誤但不要panic。

2. 建立專案

cargo new word_count_rust
cd word_count_rust

3. 撰寫主程式
這是我整理後的主程式邏輯,每個區段我都盡量加上註解來幫助自己理解。

use std::env;
use std::fs;
use std::process;

fn main() {
    // 取得命令列參數
    let args: Vec<String> = env::args().collect();

    // 如果沒有提供檔案名稱,就直接結束
    if args.len() < 2 {
        eprintln!("Usage: word_count <filename>");
        process::exit(1);
    }

    let filename = &args[1];

    // 嘗試讀取檔案,若失敗則印出錯誤訊息
    let content = fs::read_to_string(filename).unwrap_or_else(|err| {
        eprintln!("Error reading file '{}': {}", filename, err);
        process::exit(1);
    });

    // 統計資料
    let lines = content.lines().count();
    let words = content.split_whitespace().count();
    let chars = content.chars().count();

    // 印出結果
    println!("Lines: {}", lines);
    println!("Words: {}", words);
    println!("Characters: {}", chars);
}

4. 測試與除錯過程
我先建立一個簡單的文字檔 text.txt:

Rust is fast.
Rust is safe.
Rust is fun!

執行:

cargo run text.txt

輸出結果:

Lines: 3
Words: 9
Characters: 44

這邊我發現最後輸出的字元數比實際上多,後來發現是因為Windows 的文字檔用 CRLF 換行符號,每一行結尾其實佔了2個字元,所以輸出的結果會比實際上多。所以我將計算char部分的程式碼稍作修改:

let char_count: usize = content.chars().filter(|&c| c != '\r' && c != '\n').count();

這樣就只會計算到看的見的字元。
更改後的輸出結果:

Lines: 3
Words: 9
Characters: 38

若輸入錯誤檔名:

cargo run not_found.txt

輸出:

Error reading file 'not_found.txt': 系統找不到指定的檔案。 (os error 2)

表示錯誤處理邏輯也正常。

5. 最終成果展示
程式碼:

use std::env;
use std::fs;
use std::process;

fn main() {
    // 取得命令列參數
    let args: Vec<String> = env::args().collect();

    // 如果沒有提供檔案名稱,就直接結束
    if args.len() < 2 {
        eprintln!("Usage: word_count <filename>");
        process::exit(1);
    }

    let filename = &args[1];

    // 嘗試讀取檔案,若失敗則印出錯誤訊息
    let content = fs::read_to_string(filename).unwrap_or_else(|err| {
        eprintln!("Error reading file '{}': {}", filename, err);
        process::exit(1);
    });

    // 統計資料
    let lines = content.lines().count();
    let words = content.split_whitespace().count();
    let char_count: usize = content
        .chars()
        .filter(|&c| c != '\r' && c != '\n')
        .count();

    // 印出結果
    println!("Lines: {}", lines);
    println!("Words: {}", words);
    println!("Characters: {}", char_count);
}

輸入:
hello rust
this is a test
執行結果:
https://ithelp.ithome.com.tw/upload/images/20251004/20178873zuXXHpmaaX.png

6. 學習心得與補充
這次做的小專案讓我第一次真正把前面學到的知識串起來使用。從讀取命令列參數、檔案 I/O 到錯誤處理,其實每一個步驟都和前幾天的基礎緊密相關。一開始跑出來的字元數和預期不同,讓我意識到不同作業系統的換行符號也會影響計算,這個 debug 過程加深了我對字元和實際檔案內容之間差異的理解。
完成這個小專案之後,我不只是學會了如何統計字數,更重要的是體會到 Rust 的設計理念——安全與明確。每一步我都清楚知道自己在做什麼,出錯時也能快速定位。雖然這個練習很簡單,但卻讓我真正感覺到自己已經能用 Rust 做出實際有用的工具了。


上一篇
Day 19:模組與套件 (Modules & Packages)
下一篇
Day 21:錯誤處理進階與 panic! 的使用
系列文
Rust 30 天養成計畫:從零到 CLI 專案22
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言